import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Scheduler extends Setup {
	static double NORMALIZER = 100;
	static double CUT_OFF = 50;
	static byte RIGHT = 15;
	static byte LEFT = 0;

	/* Globals that make up block path */
	static String DIR = "../predictions";
	static String ALG = "ANOLU";
	static String DAT = "val";
	static int K = 1;
	static String BLOCK_PATH;

	/* End of command line globals */
	static int BEGIN = 0;
	static int END = 1000990;
	static int THREADS = 1;
	
	static String USER_DATA_SUFFIX = "";
	static String ITEM_DATA_SUFFIX = "";
	static boolean CONTINUE = false;

	public static void main(String[] args) {
		go();
	}

	static void go() {
		makeBlockPath();
		makeFolder();
		setInputPaths();
		writeLastRunSettings();
		setup();
		if(CONTINUE){
			execute(readFiles());
		} else {
			execute();
		}
	}

	static void makeBlockPath() {
		BLOCK_PATH = DIR + "/" + ALG + "/" + DAT + "/" + K + "/";
	}

	static void makeFolder() {
		if (ALG.contains("ATEST")) {
			for (int n = 0; n < COMPLICATED_HISTOGRAM.numberOfBins; n++) {
				File folder = new File(BLOCK_PATH + n + "/");
				if (!folder.exists()) {
					folder.mkdirs();
				}
			}
		} else {
			File folder = new File(BLOCK_PATH);
			if (!folder.exists()) {
				folder.mkdirs();
			}
		}
	}
	
	
	private static ArrayList<Integer> readFiles() {
		int sizePerUser = USERB[0].ilen;
		long expectedFileSize = K*sizePerUser;
		long expectedTotalSize = 1000990*sizePerUser;
		ArrayList<Integer> toBeProcessed = new ArrayList<Integer>();
		
		System.out.print("Resuming with blocks (c=corrupt, m=missing): ");

		for (int start = BEGIN; start < END; start += K) {
			String FILE_NAME = String.format("%s%07d.bin", BLOCK_PATH, start);
			File test = new File(FILE_NAME);
			
			if(test.exists()){
			//Check if the file has the right number of bytes
				if(test.length()!= expectedFileSize){
					//check if it is the right size for the last file in the folder
					if((start*sizePerUser)+test.length() != expectedTotalSize || start+K != 1000990){
						//Add to the list of blocks that still have to be processed
						toBeProcessed.add(start);
						System.out.print("c:" + start + ", ");
					}
				}
				
			} else {
			//Add to the list of blocks that still have to be processed
				toBeProcessed.add(start);
				System.out.print("m:" + start +", ");
				
			}
		}
		System.out.println();
		return toBeProcessed;
	}
	

	static void writeLastRunSettings() {
		File file = new File(DIR + "lastRunSettings.txt");
		FileWriter fw;
		try {
			fw = new FileWriter(file);
			fw.write(BLOCK_PATH + "\n");
			fw.write(THREADS);
			fw.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	static void setInputPaths() {
		Setup.FILE_PATH_USER_A = "../track1/trainIdx1.txt.ser" + USER_DATA_SUFFIX;
		Setup.FILE_PATH_ITEM_A = "../track1/trainIdx1.txt.ser.item" + ITEM_DATA_SUFFIX;
		
		if (DAT.contains("val")) {
			Setup.FILE_PATH_USER_B = "../track1/validationIdx1.txt" + ".ser";
		} else {
			Setup.FILE_PATH_USER_B = "../track1/testIdx1.txt" + ".ser";
		}
	}

	static void execute() {
		ExecutorService es = Executors.newFixedThreadPool(THREADS);
		for (int start = BEGIN; start < END; start += K) {
			int stop = start + K;
			if (stop > END) {
				stop = END;
			}
			es.execute(alg(start, stop));
		}
		es.shutdown();
	}
	
	static void execute(ArrayList<Integer> missingBlocks) {
		ExecutorService es = Executors.newFixedThreadPool(THREADS);
		for (int i = 0; i < missingBlocks.size(); i ++) {
			int start = missingBlocks.get(i);
			int stop = start + K;
			if (stop > END) {
				stop = END;
			}
			es.execute(alg(start, stop));
		}
		es.shutdown();
	}
	
	static Runnable alg(int start, int stop) {
		if (ALG.equals("AUM"))
			return new AUM(start, stop);
		else if (ALG.equals("AIM"))
			return new AIM(start, stop);
		else if (ALG.equals("AUS"))
			return new AUS(start, stop);
		else if (ALG.equals("AUSMA"))
			return new AUSMA(start, stop);
		else if (ALG.equals("AUSHASH"))
			return new AUSHASH(start, stop);
		else if (ALG.equals("AIS"))
			return new AIS(start, stop);
		else if (ALG.equals("AISMA"))
			return new AISMA(start, stop);
		else if (ALG.equals("UHIST"))
			return new UHIST(start, stop);
		else if (ALG.equals("ANOLU"))
			return new ANOLU(start, stop);
		else if (ALG.equals("COMPLICATED_HISTOGRAM"))
			return new COMPLICATED_HISTOGRAM(start, stop);
		else if (ALG.equals("AIMAL"))
			return new AIMAL(start, stop);
		else if (ALG.equals("AAAM"))
			return new AAAM(start, stop);
		else if (ALG.equals("APTS"))
			return new APTS(start, stop);
		else if (ALG.equals("AIPC"))
			return new AIPC(start, stop);
		else if (ALG.equals("AIPCfaster"))
			return new AIPCfaster(start, stop);
		else if (ALG.equals("ATPC"))
			return new ATPC(start, stop);
		else if (ALG.equals("ANV"))
			return new ANV(start, stop);
		else if (ALG.equals("ARPC"))
			return new ARPC(start, stop);
		else if (ALG.equals("ATopRPC"))
			return new ATopRPC(start, stop);
		else if (ALG.equals("ATopRAD"))
			return new ATopRAD(start, stop);
		else if (ALG.equals("AUMd"))
			return new AUMd(start, stop);
		else if (ALG.equals("AIMd"))
			return new AIMd(start, stop);
		else if (ALG.equals("AUMMM"))
			return new AUMMM(start, stop);
		else if (ALG.equals("AUT"))
			return new AUT(start, stop);
		else if (ALG.equals("AUSD"))
			return new AUSD(start, stop);
		else if (ALG.equals("AISD"))
			return new AISD(start, stop);
		else if (ALG.equals("AIT"))
			return new AIT(start, stop);
		else if (ALG.equals("AIMMM"))
			return new AIMMM(start, stop);
		else if (ALG.equals("ATopPC"))
			return new ATopPC(start, stop);
		else if (ALG.equals("ATopAD"))
			return new ATopAD(start, stop);
		else if (ALG.equals("AWTopPC"))
			return new AWTopPC(start, stop);
		else if (ALG.equals("AWTopAD"))
			return new AWTopAD(start, stop);
		else if (ALG.equals("AWTopCPC"))
			return new AWTopCPC(start, stop);
		else if (ALG.equals("ATopCPC"))
			return new ATopCPC(start, stop);
		else if (ALG.equals("ATopMSD"))
			return new ATopMSD(start, stop);
		else if (ALG.equals("AUOT"))
			return new AUOT(start, stop);
		else if (ALG.equals("AIOT"))
			return new AIOT(start, stop);
		else if (ALG.equals("AUOP"))
			return new AUOP(start, stop);
		else if (ALG.equals("AIOP"))
			return new AIOP(start, stop);
		else if (ALG.equals("AWTopMSD"))
			return new AWTopMSD(start, stop);
		else if (ALG.equals("AMSD"))
			return new AMSD(start, stop);
		/* You forgot to add your Algorithm ERROR! */
		else
			System.out.println("Forgot to add your Algorithm: " + ALG);
		return null;
	}
}
